{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using `partial_fit()` with grid search\n",
    "`GridSearchCV` doesn't have a `partial_fit()` method, but we can use `GridSearchCV` to find the best initial hyperparameters for our models before moving to `partial_fit()`.\n",
    "\n",
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import sqlite3\n",
    "\n",
    "with sqlite3.connect('../../ch_11/logs/logs.db') as conn:\n",
    "    logs_2018 = pd.read_sql(\n",
    "        'SELECT * FROM logs WHERE datetime BETWEEN \"2018-01-01\" AND \"2019-01-01\";', \n",
    "        conn, parse_dates=['datetime'], index_col='datetime'\n",
    "    )\n",
    "    hackers_2018 = pd.read_sql(\n",
    "        'SELECT * FROM attacks WHERE start BETWEEN \"2018-01-01\" AND \"2019-01-01\";', \n",
    "        conn, parse_dates=['start', 'end']\n",
    "    ).assign(\n",
    "        duration=lambda x: x.end - x.start, \n",
    "        start_floor=lambda x: x.start.dt.floor('min'),\n",
    "        end_ceil=lambda x: x.end.dt.ceil('min')\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get training and testing sets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_X(log, day):\n",
    "    \"\"\"\n",
    "    Get data we can use for the X\n",
    "    \n",
    "    Parameters:\n",
    "        - log: The logs dataframe\n",
    "        - day: A day or single value we can use as a datetime index slice\n",
    "    \n",
    "    Returns: \n",
    "        A pandas DataFrame\n",
    "    \"\"\"\n",
    "    return pd.get_dummies(log[day].assign(\n",
    "        failures=lambda x: 1 - x.success\n",
    "    ).query('failures > 0').resample('1min').agg(\n",
    "        {'username':'nunique', 'failures': 'sum'}\n",
    "    ).dropna().rename(\n",
    "        columns={'username':'usernames_with_failures'}\n",
    "    ).assign(\n",
    "        day_of_week=lambda x: x.index.dayofweek, \n",
    "        hour=lambda x: x.index.hour\n",
    "    ).drop(columns=['failures']), columns=['day_of_week', 'hour'])\n",
    "\n",
    "def get_y(datetimes, hackers, resolution='1min'):\n",
    "    \"\"\"\n",
    "    Get data we can use for the y (whether or not a hacker attempted a log in during that time).\n",
    "    \n",
    "    Parameters:\n",
    "        - datetimes: The datetimes to check for hackers\n",
    "        - hackers: The dataframe indicating when the attacks started and stopped\n",
    "        - resolution: The granularity of the datetime. Default is 1 minute.\n",
    "        \n",
    "    Returns:\n",
    "        A pandas Series of booleans.\n",
    "    \"\"\"\n",
    "    date_ranges = hackers.apply(\n",
    "        lambda x: pd.date_range(x.start_floor, x.end_ceil, freq=resolution), \n",
    "        axis=1\n",
    "    )\n",
    "    dates = pd.Series()\n",
    "    for date_range in date_ranges:\n",
    "        dates = pd.concat([dates, date_range.to_series()])\n",
    "    return datetimes.isin(dates)\n",
    "\n",
    "def get_X_y(log, day, hackers):\n",
    "    \"\"\"\n",
    "    Get the X, y data to build a model with.\n",
    "    \n",
    "    Parameters:\n",
    "        - log: The logs dataframe\n",
    "        - day: A day or single value we can use as a datetime index slice\n",
    "        - hackers: The dataframe indicating when the attacks started and stopped\n",
    "        \n",
    "    Returns:\n",
    "        X, y tuple where X is a pandas DataFrame and y is a pandas Series\n",
    "    \"\"\"\n",
    "    X = get_X(log, day)\n",
    "    y = get_y(X.reset_index().datetime, hackers)\n",
    "    return X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_jan, y_jan = get_X_y(logs_2018, '2018-01', hackers_2018)\n",
    "X_feb, y_feb = get_X_y(logs_2018, '2018-02', hackers_2018)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Run GridSearchCV to build a Passive Aggressive Classifier\n",
    "Train on January 2018."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture\n",
    "from sklearn.linear_model import PassiveAggressiveClassifier\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from ml_utils.partial_fit_pipeline import PartialFitPipeline\n",
    "\n",
    "pipeline = PartialFitPipeline([\n",
    "    ('scale', StandardScaler()), \n",
    "    ('pa', PassiveAggressiveClassifier(random_state=0, max_iter=1000, tol=1e-3))\n",
    "])\n",
    "\n",
    "search_space = {\n",
    "    'pa__C' : [0.01, 0.1, 1, 10],\n",
    "    'pa__fit_intercept' : [True, False],\n",
    "    'pa__class_weight' : ['balanced', None]\n",
    "}\n",
    "\n",
    "pa_grid = GridSearchCV(pipeline, search_space, cv=5, scoring='f1_macro').fit(X_jan, y_jan)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'pa__C': 0.1, 'pa__class_weight': None, 'pa__fit_intercept': True}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pa_grid.best_params_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Evaluate the model's initial performance (on February)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\molinstefanie\\packt\\venv\\lib\\site-packages\\sklearn\\pipeline.py:331: DataConversionWarning: Data with input dtype uint8, int64 were all converted to float64 by StandardScaler.\n",
      "  Xt = transform.transform(Xt)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "False    37790\n",
       "True        13\n",
       "dtype: int64"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "preds = pa_grid.predict(X_feb)\n",
    "pd.Series(preds).value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "       False       1.00      1.00      1.00     37787\n",
      "        True       0.92      0.75      0.83        16\n",
      "\n",
      "   micro avg       1.00      1.00      1.00     37803\n",
      "   macro avg       0.96      0.87      0.91     37803\n",
      "weighted avg       1.00      1.00      1.00     37803\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report\n",
    "\n",
    "print(classification_report(y_feb, preds))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x12e44b50>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from ml_utils.classification import confusion_matrix_visual\n",
    "\n",
    "confusion_matrix_visual(y_feb, preds, class_labels=[False, True])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Store the best estimator for `partial_fit()` usage later"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "pa = pa_grid.best_estimator_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Update the model with the February 2018 data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\molinstefanie\\packt\\venv\\lib\\site-packages\\sklearn\\preprocessing\\data.py:645: DataConversionWarning: Data with input dtype uint8, int64 were all converted to float64 by StandardScaler.\n",
      "  return self.partial_fit(X, y)\n",
      "c:\\users\\molinstefanie\\packt\\venv\\lib\\site-packages\\sklearn\\base.py:464: DataConversionWarning: Data with input dtype uint8, int64 were all converted to float64 by StandardScaler.\n",
      "  return self.fit(X, **fit_params).transform(X)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "PartialFitPipeline(memory=None,\n",
       "          steps=[('scale', StandardScaler(copy=True, with_mean=True, with_std=True)), ('pa', PassiveAggressiveClassifier(C=0.1, average=False, class_weight=None,\n",
       "              early_stopping=False, fit_intercept=True, loss='hinge',\n",
       "              max_iter=1000, n_iter=None, n_iter_no_change=5, n_jobs=None,\n",
       "              random_state=0, shuffle=True, tol=0.001,\n",
       "              validation_fraction=0.1, verbose=0, warm_start=False))])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pa.partial_fit(X_feb, y_feb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Evaluate model on March through June 2018 data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\users\\molinstefanie\\packt\\venv\\lib\\site-packages\\sklearn\\pipeline.py:331: DataConversionWarning: Data with input dtype uint8, int64 were all converted to float64 by StandardScaler.\n",
      "  Xt = transform.transform(Xt)\n"
     ]
    }
   ],
   "source": [
    "X_mar, y_mar = get_X_y(logs_2018, '2018-03', hackers_2018)\n",
    "X_q2, y_q2 = get_X_y(logs_2018, '2018-Q2', hackers_2018)\n",
    "X_eval = pd.concat([X_mar, X_q2])\n",
    "y_eval = pd.concat([y_mar, y_q2])\n",
    "preds = pa.predict(X_eval)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "       False       1.00      1.00      1.00    168783\n",
      "        True       1.00      0.83      0.91        12\n",
      "\n",
      "   micro avg       1.00      1.00      1.00    168795\n",
      "   macro avg       1.00      0.92      0.95    168795\n",
      "weighted avg       1.00      1.00      1.00    168795\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report\n",
    "\n",
    "print(classification_report(y_eval, preds))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x10974f0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from ml_utils.classification import confusion_matrix_visual\n",
    "\n",
    "confusion_matrix_visual(y_eval, preds, class_labels=[False, True])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
